#include <libdswav/dswvideo_codec.h>

#define GM_MAIN_STREAM 3
#define GM_SUB_STREAM 2

#define GM_MODE GM_EVBR//GM_CBR	//GM_EVBR
#define DSW_GOP_FIX 1

CDswvideo_codec::CDswvideo_codec(LPST_ENV_T pEnv, int nMemSize)
: CDswmemlistbase(pEnv)
, m_pMemory(NULL)
, m_nEncThread(INVALID_PTHREAD_ID)
, m_nCapThread(INVALID_PTHREAD_ID)
{
	LOG4CPLUS_DEBUG(log_dsw, "Create CDswvideo_codec class!");
	memset(m_pOsds, NULL, sizeof(m_pOsds));
	m_pMemory = new CDswmemory(1024, nMemSize);
	memset(&m_nPalette, 0, sizeof(m_nPalette));
	InitList();
	StartStream();
	StartCapThread();
}

CDswvideo_codec::~CDswvideo_codec()
{
	StopCapThread();
	StopStream();
	UnInitList();
	if (m_pMemory)
		delete m_pMemory;
	LOG4CPLUS_DEBUG(log_dsw, "Destroy CDswvideo_codec class!");
}

void CDswvideo_codec::InitList()
{
	for (int nCh = 0; nCh < MAX_CHANNEL_LOCAL; nCh++){
		for (int nSubCh = 0; nSubCh < MAX_V_BITSTREAM_NUM; nSubCh++){
			pthread_mutex_init(&m_nUnUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh].lock, NULL);
			pthread_mutex_init(&m_nUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh].lock, NULL);
			m_nUnUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh].seq = 
				m_nUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh].seq = 0;
			for (int i = 0; i < MAX_VIDEO_FRAMES; i++){
				LPST_VIDEO_FRAME_T pFrame = new ST_VIDEO_FRAME_T;
				memset(pFrame, 0, sizeof(ST_VIDEO_FRAME_T));
				PutNodeToList((void*)pFrame, &m_nUnUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh]);
			}
		}
	}
}

void CDswvideo_codec::UnInitList()
{
	for (int nCh = 0; nCh < MAX_CHANNEL_LOCAL; nCh++){
		for (int nSubCh = 0; nSubCh < MAX_V_BITSTREAM_NUM; nSubCh++){
			LPST_VIDEO_FRAME_T pNode = NULL;
			pNode = (LPST_VIDEO_FRAME_T)GetNodeFromList(&m_nUnUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh]);
			while(pNode){
				if (pNode->pFrame)
					m_pMemory->DswMemoryDelete(pNode->pFrame);
				delete pNode;
				pNode = (LPST_VIDEO_FRAME_T)GetNodeFromList(&m_nUnUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh]);
			}
			pNode = (LPST_VIDEO_FRAME_T)GetNodeFromList(&m_nUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh]);
			while(pNode){
				if (pNode->pFrame)
					m_pMemory->DswMemoryDelete(pNode->pFrame);
				delete pNode;
				pNode = (LPST_VIDEO_FRAME_T)GetNodeFromList(&m_nUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh]);
			}
			pthread_mutex_destroy(&m_nUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh].lock);
			pthread_mutex_destroy(&m_nUnUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh].lock);
		}
	}
}

void CDswvideo_codec::StartStream()
{
	m_groupfd = gm_new_groupfd();
	int nCh;
	ST_VIDEO_PARAM nVideoParam;

	SendMessage(PMSG_GET_VIDEO_PARAM, (void*)&nVideoParam);
	memcpy(&m_nRuntimeParam, &nVideoParam, sizeof(nVideoParam));
	for (nCh = 0; nCh < MAX_CHANNEL_LOCAL; nCh++){
		DECLARE_ATTR(cap_attr, gm_cap_attr_t);
		DECLARE_ATTR(h264e_attr, gm_h264e_attr_t);
		DECLARE_ATTR(cap_3di_attr, gm_3di_attr_t);
		DECLARE_ATTR(dnr_attr, gm_3dnr_attr_t);

		m_capture_object[nCh] = gm_new_obj(GM_CAP_OBJECT);
		m_main_enc_object[nCh] = gm_new_obj(GM_ENCODER_OBJECT);

		cap_attr.cap_vch = nCh;
		cap_attr.path = GM_SUB_STREAM;
		cap_attr.enable_mv_data = 0;
		gm_set_attr(m_capture_object[nCh], &cap_attr);

#if 1
		cap_3di_attr.deinterlace = 1;
		cap_3di_attr.denoise = 1;
		gm_set_attr(m_capture_object[nCh], &cap_3di_attr);

		dnr_attr.enabled = 1;
		gm_set_attr(m_capture_object[nCh], &dnr_attr);
#else
		cap_3di_attr.deinterlace = 1;
		cap_3di_attr.denoise = 0;
		gm_set_attr(m_capture_object[nCh], &cap_3di_attr);
/*
		dnr_attr.enabled = 1;
		gm_set_attr(m_capture_object[nCh], &dnr_attr);
*/
#endif
		h264e_attr.dim.width = defRESOLUTIONS[nVideoParam.nVParam[nCh].bs[MAIN_STREAM].size].width;
		h264e_attr.dim.height = defRESOLUTIONS[nVideoParam.nVParam[nCh].bs[MAIN_STREAM].size].height;
		h264e_attr.frame_info.framerate = nVideoParam.nVParam[nCh].bs[MAIN_STREAM].fps;
		h264e_attr.ratectl.mode = GM_EVBR;
		h264e_attr.ratectl.gop = nVideoParam.nVParam[nCh].bs[MAIN_STREAM].gop;
		h264e_attr.ratectl.bitrate = nVideoParam.nVParam[nCh].bs[MAIN_STREAM].bps;
		h264e_attr.ratectl.min_quant = 20;
		if (nVideoParam.nVParam[nCh].bs[MAIN_STREAM].quality >= 3)
			h264e_attr.ratectl.init_quant = 25;
		else if ((nVideoParam.nVParam[nCh].bs[MAIN_STREAM].quality >= 2) && (nVideoParam.nVParam[nCh].bs[MAIN_STREAM].quality < 3))
			h264e_attr.ratectl.init_quant = 30;
		else
			h264e_attr.ratectl.init_quant = 45;
		h264e_attr.ratectl.max_quant = 51;
		h264e_attr.ratectl.bitrate_max = nVideoParam.nVParam[nCh].bs[MAIN_STREAM].bps;
		h264e_attr.b_frame_num = 0;
		h264e_attr.enable_mv_data = 0;
		h264e_attr.ratectl.gop = nVideoParam.nVParam[nCh].bs[MAIN_STREAM].fps;
		gm_set_attr(m_main_enc_object[nCh], &h264e_attr);

		m_main_bindfd[nCh] = gm_bind(m_groupfd, m_capture_object[nCh], m_main_enc_object[nCh]);
		LOG4CPLUS_DEBUG(log_dsw, "Main stream: " << defRESOLUTIONS[nVideoParam.nVParam[nCh].bs[MAIN_STREAM].size].width << " X " << defRESOLUTIONS[nVideoParam.nVParam[nCh].bs[MAIN_STREAM].size].height);

		m_sub_enc_object[nCh] = gm_new_obj(GM_ENCODER_OBJECT);
	
		h264e_attr.dim.width = defRESOLUTIONS[nVideoParam.nVParam[nCh].bs[SUB_STREAM].size].width;
		h264e_attr.dim.height = defRESOLUTIONS[nVideoParam.nVParam[nCh].bs[SUB_STREAM].size].height;
		h264e_attr.frame_info.framerate = nVideoParam.nVParam[nCh].bs[SUB_STREAM].fps;
		h264e_attr.ratectl.gop = nVideoParam.nVParam[nCh].bs[SUB_STREAM].gop;
		h264e_attr.ratectl.mode = GM_EVBR;
		h264e_attr.ratectl.bitrate = nVideoParam.nVParam[nCh].bs[SUB_STREAM].bps;
		h264e_attr.ratectl.min_quant = 20;
		if (nVideoParam.nVParam[nCh].bs[SUB_STREAM].quality >= 3)
			h264e_attr.ratectl.init_quant = 25;
		else if ((nVideoParam.nVParam[nCh].bs[SUB_STREAM].quality >= 2) && (nVideoParam.nVParam[nCh].bs[SUB_STREAM].quality < 3))
			h264e_attr.ratectl.init_quant = 30;
		else
			h264e_attr.ratectl.init_quant = 45;
		h264e_attr.ratectl.max_quant = 51;
		h264e_attr.ratectl.bitrate_max = nVideoParam.nVParam[nCh].bs[SUB_STREAM].bps;
		h264e_attr.ratectl.gop = nVideoParam.nVParam[nCh].bs[SUB_STREAM].fps * 2;
		gm_set_attr(m_sub_enc_object[nCh], &h264e_attr);
		m_sub_bindfd[nCh] = gm_bind(m_groupfd, m_capture_object[nCh], m_sub_enc_object[nCh]);
		LOG4CPLUS_DEBUG(log_dsw, "Sub stream: " << defRESOLUTIONS[nVideoParam.nVParam[nCh].bs[SUB_STREAM].size].width << " X " << defRESOLUTIONS[nVideoParam.nVParam[nCh].bs[SUB_STREAM].size].height);

		if (gm_apply(m_groupfd) < 0){
			LOG4CPLUS_ERROR(log_dsw, "gm_apply fail, AP procedure something wrong!");
		}

		m_pOsds[nCh] = new CDswosd(m_pEnv, m_capture_object[nCh], m_main_bindfd[nCh], nCh, &m_nPalette);
	}

	pthread_create(&m_nEncThread, NULL, CDswvideo_codec::cbEncThreadFunc, (void *)this);
}

void CDswvideo_codec::StopStream()
{
	if (m_nEncThread != INVALID_PTHREAD_ID)
		pthread_join(m_nEncThread, NULL);
	for (int i = 0; i < MAX_CHANNEL_LOCAL; i++){
		if (m_pOsds[i])
			delete m_pOsds[i];
		gm_unbind(m_main_bindfd[i]);
		gm_unbind(m_sub_bindfd[i]);
		gm_apply(m_groupfd);
		gm_delete_obj(m_main_enc_object[i]);
		gm_delete_obj(m_sub_enc_object[i]);
		gm_delete_obj(m_capture_object[i]);
	}
	gm_delete_groupfd(m_groupfd);
}

typedef struct{
	unsigned int ntime;
	int nlen;
} ST_COUNT_FPS_T, *LPST_COUNT_FPS_T;

void CDswvideo_codec::EncThreadFunc()
{
	LOG4CPLUS_DEBUG(log_dsw, "Start Video encode thread!");
	char *bitstream_data[MAX_CHANNEL_LOCAL][MAX_V_BITSTREAM_NUM];
	int bitstream_datalen[MAX_CHANNEL_LOCAL][MAX_V_BITSTREAM_NUM];
	gm_pollfd_t poll_fds[MAX_CHANNEL_LOCAL * MAX_V_BITSTREAM_NUM];
	gm_enc_multi_bitstream_t multi_bs[MAX_CHANNEL_LOCAL * MAX_V_BITSTREAM_NUM];
	int nMaxBitLength[MAX_CHANNEL_LOCAL][MAX_V_BITSTREAM_NUM];
	int i, j, ret;

	std::list<LPST_COUNT_FPS_T> nCh0, nCh1;
	time_t nstart_ch0, nstart_ch1;

	for (j = 0; j < MAX_CHANNEL_LOCAL; j++){
		for (i = 0; i < MAX_V_BITSTREAM_NUM; i++){
			if (i == MAIN_STREAM){
				bitstream_data[j][i] = new char[defRESOLUTIONS[MAIN_STREAM].width * defRESOLUTIONS[MAIN_STREAM].width * 3 / 2];
				nMaxBitLength[j][i] = defRESOLUTIONS[MAIN_STREAM].width * defRESOLUTIONS[MAIN_STREAM].width * 3 / 2;
			}
			else{
				bitstream_data[j][i] = new char[defRESOLUTIONS[SUB_STREAM].width * defRESOLUTIONS[SUB_STREAM].width * 3 / 2];
				nMaxBitLength[j][i] = defRESOLUTIONS[SUB_STREAM].width * defRESOLUTIONS[SUB_STREAM].width * 3 / 2;
			}
			bitstream_datalen[j][i] = 0;
		}
	}
	memset(poll_fds, 0, sizeof(poll_fds));
	for (j = 0; j < MAX_CHANNEL_LOCAL; j++){
		poll_fds[j * MAX_V_BITSTREAM_NUM + MAIN_STREAM].bindfd = m_main_bindfd[j];
		poll_fds[j * MAX_V_BITSTREAM_NUM + MAIN_STREAM].event = GM_POLL_READ;
		poll_fds[j * MAX_V_BITSTREAM_NUM + SUB_STREAM].bindfd = m_sub_bindfd[j];
		poll_fds[j * MAX_V_BITSTREAM_NUM + SUB_STREAM].event = GM_POLL_READ;
	}

	while(!m_pEnv->bExit){
		ret = gm_poll(poll_fds, MAX_V_BITSTREAM_NUM * MAX_VIDEO_CHANNEL, 500);
		if (ret == GM_TIMEOUT) {
			LOG4CPLUS_WARN(log_dsw, "Poll video timeout!!");
			continue;
		}
		if (ret != GM_SUCCESS) {
			LOG4CPLUS_WARN(log_dsw, "Poll error!! ret = [ " << ret << " ]");
			continue;
		}
		memset(multi_bs, 0, sizeof(multi_bs));
		for (j = 0; j < MAX_CHANNEL_LOCAL; j++){
			for (i = 0; i < MAX_V_BITSTREAM_NUM; i++) {
				if (poll_fds[j * MAX_V_BITSTREAM_NUM + i].revent.event != GM_POLL_READ){
					continue;
				}
				if (poll_fds[j * MAX_V_BITSTREAM_NUM + i].revent.bs_len > nMaxBitLength[j][i]) {
					LOG4CPLUS_ERROR(log_dsw, "bitstream buffer length is not enough!" << poll_fds[j * MAX_V_BITSTREAM_NUM + i].revent.bs_len << ", " << nMaxBitLength[j][i]);
					if (gm_request_keyframe(poll_fds[j * MAX_V_BITSTREAM_NUM + i].bindfd) < 0)
						LOG4CPLUS_ERROR(log_dsw, "CH[" << j << "] streams [" << i << "] request keyframe failed!");
					continue;
				}
				multi_bs[j * MAX_V_BITSTREAM_NUM + i].bindfd = poll_fds[j * MAX_V_BITSTREAM_NUM + i].bindfd;
				multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_buf = bitstream_data[j][i];
				multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_buf_len = nMaxBitLength[j][i];
				multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.mv_buf = 0;
				multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.mv_buf_len = 0;
			}
		}
		if ((ret = gm_recv_multi_bitstreams(multi_bs, MAX_V_BITSTREAM_NUM * MAX_VIDEO_CHANNEL)) < 0) {
			LOG4CPLUS_ERROR(log_dsw, "Error return value! value = " << ret);
		}
		else {
			for (j = 0; j < MAX_CHANNEL_LOCAL; j++){
				for (i = 0; i < MAX_V_BITSTREAM_NUM; i++) {
					if ((multi_bs[j * MAX_V_BITSTREAM_NUM + i].retval < 0) && multi_bs[j * MAX_V_BITSTREAM_NUM + i].bindfd) {
						LOG4CPLUS_ERROR(log_dsw, "CH[" << j << "] streams [" << i << "] Error to receive bitstream. ret = " << multi_bs[j * MAX_V_BITSTREAM_NUM + i].retval);
						if (gm_request_keyframe(multi_bs[j * MAX_V_BITSTREAM_NUM + i].bindfd) < 0)
							LOG4CPLUS_ERROR(log_dsw, "CH[" << j << "] streams [" << i << "] request keyframe failed!");
						continue;
					}
					else if (multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_len > V_BITSTREAM_LEN){
						LOG4CPLUS_ERROR(log_dsw, "CH[" << j << "] streams [" << i << "] Invalid frame length!");
						if (gm_request_keyframe(multi_bs[j * MAX_V_BITSTREAM_NUM + i].bindfd) < 0)
							LOG4CPLUS_ERROR(log_dsw, "CH[" << j << "] streams [" << i << "] request keyframe failed!");
						continue;
					}
					else if (multi_bs[j * MAX_V_BITSTREAM_NUM + i].retval == GM_SUCCESS) {
#if 0
						{
							if ((j == 0) && (i == 0)){
								if (multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.keyframe == 1){
									FILE* fp = fopen("test.h264", "wb");
									char sbuf[128];
									sprintf(sbuf, "len = %d\n", multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_len);
									fwrite(sbuf, 1, strlen(sbuf), fp);
									int len = multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_len;
									int kk;
									char* p = multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_buf;
									while(len >= 16){
										memset(sbuf, 0, sizeof(sbuf));
										for (kk = 0; kk < 16; kk++, p++)
											sprintf(sbuf + strlen(sbuf), "0x%02x, ", p[0]);
										fwrite(sbuf, 1, strlen(sbuf), fp);
										fwrite("\n", 1, 1, fp);
										len -= 16;
									}
									if (len > 0){
										memset(sbuf, 0, sizeof(sbuf));
										for (kk = 0; kk < len; kk++, p++)
											sprintf(sbuf + strlen(sbuf), "0x%02x, ", p[0]);
										fwrite(sbuf, 1, strlen(sbuf), fp);
										fwrite("\n", 1, 1, fp);
									}
									printf("kkkkkkkkkkkk = %d\n", multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_len);
									fclose(fp);
								}
							}
						}
#endif						
						LPST_VIDEO_FRAME_T pFrame = (LPST_VIDEO_FRAME_T)GetNodeFromList(&m_nUnUsed[j * MAX_V_BITSTREAM_NUM + i]);
						if (pFrame == NULL){
RE_FREE:							
							pFrame = (LPST_VIDEO_FRAME_T)GetNodeFromList(&m_nUsed[j * MAX_V_BITSTREAM_NUM + i]);
						}
						if (pFrame->pFrame){
							m_pMemory->DswMemoryDelete(pFrame->pFrame);
							pFrame->pFrame = NULL;
						}
						pFrame->pFrame = m_pMemory->DswMemoryNew(multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_len);
						if (!pFrame->pFrame){
							LOG4CPLUS_ERROR(log_dsw, "CH[" << j << "] streams [" << i << "] has not enough frame memory! bs len = " << 
								multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_len);
							PutNodeToList(pFrame, &m_nUnUsed[j * MAX_V_BITSTREAM_NUM + i]);
							goto RE_FREE;
							continue;
						}
						m_pMemory->DswMemoryCpy(multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_buf, 
							multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_len, pFrame->pFrame);

						pFrame->ch_num = j;
						pFrame->sub_num = i;

						if (multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.newbs_flag & GM_FLAG_NEW_DIM){
							pFrame->is_newbs = true;
							pFrame->nSize = m_nRuntimeParam.nVParam[j].bs[i].size;
							pFrame->nFps = m_nRuntimeParam.nVParam[j].bs[i].fps;
						}
						else if (multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.newbs_flag & GM_FLAG_NEW_FRAME_RATE){
							pFrame->is_newfps = true;
							pFrame->nSize = m_nRuntimeParam.nVParam[j].bs[i].size;
							pFrame->nFps = m_nRuntimeParam.nVParam[j].bs[i].fps;
						}
						else{
							pFrame->is_newbs = false;
							pFrame->is_newfps = false;
						}

						pFrame->is_keyframe = (multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.keyframe == 1);
						if (pFrame->is_keyframe){
							pFrame->nSize = m_nRuntimeParam.nVParam[j].bs[i].size;
							pFrame->nFps = m_nRuntimeParam.nVParam[j].bs[i].fps;
						}
						MUTEX_LOCK(&m_nUsed[j * MAX_V_BITSTREAM_NUM + i].lock);
						pFrame->seq = m_nUsed[j * MAX_V_BITSTREAM_NUM + i].seq++;
						MUTEX_UNLOCK(&m_nUsed[j * MAX_V_BITSTREAM_NUM + i].lock);
						pFrame->length = multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_len;
						gettimeofday(&pFrame->timestamp, NULL);

#if 0
						if ((j == 0) && (i == MAIN_STREAM)){
							LPST_COUNT_FPS_T pNode = new ST_COUNT_FPS_T;
							pNode->nlen = multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_len;
							pNode->ntime = multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.timestamp;
							nCh0.push_back(pNode);
						}
						if ((j == 1) && (i == MAIN_STREAM)){
							LPST_COUNT_FPS_T pNode = new ST_COUNT_FPS_T;
							pNode->nlen = multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.bs_len;
							pNode->ntime = multi_bs[j * MAX_V_BITSTREAM_NUM + i].bs.timestamp;
							nCh1.push_back(pNode);
						}
						if (nCh0.size() >= 25){
							std::list<LPST_COUNT_FPS_T>::iterator it;
							int nbps = 0;
							unsigned int nstart, nend;
							nstart = (nCh0.front())->ntime;
							nend = (nCh0.back())->ntime;
							for (it = nCh0.begin(); it != nCh0.end(); it++){
								nbps += (*it)->nlen * 8;
							}
							LPST_COUNT_FPS_T pNode = nCh0.front();
							nCh0.pop_front();
							delete pNode;
							if (nstart_ch0 != time(NULL)){
								printf("ch0 bps = %.1f Bps\n", (double)nbps / ((double)(nend - nstart) / 1000));
								nstart_ch0 = time(NULL);
							}
						}
						if (nCh1.size() >= (25 * 5)){
							std::list<LPST_COUNT_FPS_T>::iterator it;
							int nbps = 0;
							unsigned int nstart, nend;
							nstart = (nCh1.front())->ntime;
							nend = (nCh1.back())->ntime;
							for (it = nCh1.begin(); it != nCh1.end(); it++){
								nbps += (*it)->nlen * 8;
							}
							LPST_COUNT_FPS_T pNode = nCh1.front();
							nCh1.pop_front();
							delete pNode;
//							if (nstart_ch1 != time(NULL)){
								printf("--------------------------ch1 bps = %.1f Bps, time = %.1f sec, count = %d\n", (double)nbps / 5, ((double)(nend - nstart) / 1000), nCh1.size());
								nstart_ch1 = time(NULL);
	//						}
						}
#endif
						PutNodeToList(pFrame, &m_nUsed[j * MAX_V_BITSTREAM_NUM + i]);
					}
				}
			}
		}
	}
#if 1
	while(!nCh0.empty()){
		LPST_COUNT_FPS_T pNode = nCh0.front();
		nCh0.pop_front();
		delete pNode;
	}
	while(!nCh1.empty()){
		LPST_COUNT_FPS_T pNode = nCh1.front();
		nCh1.pop_front();
		delete pNode;
	}
#endif
	for (j = 0; j < MAX_CHANNEL_LOCAL; j++){
		for (i = 0; i < MAX_V_BITSTREAM_NUM; i++){
			if (bitstream_data[j][i] != NULL)
				delete bitstream_data[j][i];
		}
	}
	LOG4CPLUS_DEBUG(log_dsw, "Exit Video encode thread!");
}

bool CDswvideo_codec::DumpFrame(void* pSrc, void* pDest)
{
	LPST_VIDEO_FRAME_T pSrcFrame = (LPST_VIDEO_FRAME_T)pSrc;
	LPST_VIDEO_FRAME_T pDestFrame = (LPST_VIDEO_FRAME_T)pDest;
	if (pSrcFrame->length > pDestFrame->length)
		return false;
	int nCpBytes = m_pMemory->DswMemoryCpy(pSrcFrame->pFrame, (char*)pDestFrame->pFrame, pDestFrame->length);
	memcpy(pDestFrame, pSrcFrame, sizeof(ST_VIDEO_FRAME_T) - sizeof(void*));
	return (nCpBytes == pSrcFrame->length);
}

bool CDswvideo_codec::GetVideoFrame(LPST_VIDEO_FRAME_T pFrame)
{
	int nCh = pFrame->ch_num;
	int nSubCh = pFrame->sub_num;
	bool bRet = CopyNodeFromList(&m_nUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh], (void*)pFrame, TYPE_VIDEO_FRAME);
	return bRet;
}

void CDswvideo_codec::StartCapThread()
{
	pthread_mutex_init(&m_nCapNodeList_lock, NULL);
	for (int i = 0; i < MAX_ALARM_NODE_NUM; i++){
		LPST_ALARM_NODE_T pNode = new ST_ALARM_NODE_T;
		memset(pNode, 0, sizeof(ST_ALARM_NODE_T));
		pNode->pJpegData = new char[MAX_SNAPHSOT_LEN];
		m_nCapNodeList_UnUsed.push_back(pNode);
	}
	pthread_create(&m_nCapThread, NULL, CDswvideo_codec::cbCapThreadFunc, (void *)this);
}

void CDswvideo_codec::StopCapThread()
{
	if (m_nCapThread != INVALID_PTHREAD_ID)
		pthread_join(m_nCapThread, NULL);
	CLEAR_ALARM_NODE(m_nCapNodeList);
	CLEAR_ALARM_NODE(m_nCapNodeList_UnUsed);
	pthread_mutex_destroy(&m_nCapNodeList_lock);
}

LPST_ALARM_NODE_T CDswvideo_codec::GetCapNode(LPLIST_ST_ALARM_NODE_T pList)
{
	LPST_ALARM_NODE_T pNode = NULL;
	MUTEX_LOCK(&m_nCapNodeList_lock);
	if (!pList->empty()){
		pNode = pList->front();
		pList->pop_front();
	}
	MUTEX_UNLOCK(&m_nCapNodeList_lock);
	return pNode;
}

void CDswvideo_codec::PutCapNode(LPST_ALARM_NODE_T pNode, LPLIST_ST_ALARM_NODE_T pList)
{
	MUTEX_LOCK(&m_nCapNodeList_lock);
	pList->push_back(pNode);
	MUTEX_UNLOCK(&m_nCapNodeList_lock);
}

void CDswvideo_codec::CapThreadFunc()
{
	LOG4CPLUS_DEBUG(log_dsw, "Begin pic capture thread!");
	while(!m_pEnv->bExit){
		LPST_ALARM_NODE_T pNode = GetCapNode(&m_nCapNodeList);
		if (pNode == NULL){
			usleep(1000 * 10);
			continue;
		}
		snapshot_t snapshot;
		snapshot.image_quality = 70;
		snapshot.bs_buf = pNode->pJpegData;
		snapshot.bs_buf_len = MAX_SNAPHSOT_LEN;
		snapshot.bindfd = m_main_bindfd[pNode->nBindV & ~MASK_BINDV_EXTCAM];
		snapshot.bs_width = defRESOLUTIONS[MAIN_STREAM].width;
		snapshot.bs_height =  defRESOLUTIONS[MAIN_STREAM].height;
		int snapshot_len = gm_request_snapshot(&snapshot, 500);
		if (snapshot_len > 0){
			pNode->nPayloadSize = snapshot_len;
			pNode->nPayloadType = EN_PIC;
			LOG4CPLUS_DEBUG(log_dsw, "Alarm capture pic [ " << (pNode->nBindV & ~MASK_BINDV_EXTCAM) << " ] finished, and send message to server client!");
			SendMessage(PMSG_SET_ALARMCAP_FINISHED, (void*)pNode);
		}
		else	{
			LOG4CPLUS_DEBUG(log_dsw, "Alarm capture pic [ " << (pNode->nBindV & ~MASK_BINDV_EXTCAM) << " ] failed!");
		}
		PutCapNode(pNode, &m_nCapNodeList_UnUsed);
	}
	LOG4CPLUS_DEBUG(log_dsw, "Exit pic capture thread!");
}

int CDswvideo_codec::OnMessage(EN_MSG_TYPE nMsgType, void * pParam)
{
	switch(nMsgType){
	case PMSG_SET_FORCE_IFRAME:
		{
			int nStream = *((int*)pParam);
			if (nStream == MAIN_STREAM)
				gm_request_keyframe(m_main_bindfd[0]);
			else
				gm_request_keyframe(m_sub_bindfd[0]);
		}
		break;
	case PMSG_SET_SIGNALCHANGE:
		{
			int nCh;
			for (nCh = 0; nCh < MAX_CHANNEL_LOCAL; nCh++){
				LPST_SIGNAL_T pSignal = (LPST_SIGNAL_T)pParam;
				if (m_pOsds[nCh] && (pSignal->nMask & (1 << nCh)))
					m_pOsds[nCh]->HandleMessage(nMsgType, pParam);
			}
		}
		break;
	case PMSG_UPDATE_VIDEO_PARAM:
		{
			LPST_VIDEO_PARAM pVideoParam = (LPST_VIDEO_PARAM)pParam;
			int nCh, nSubCh;
			DECLARE_ATTR(h264e_attr, gm_h264e_attr_t);
			
			for (nCh = 0; nCh < MAX_CHANNEL_LOCAL; nCh++){
				for (nSubCh = 0; nSubCh < MAX_V_BITSTREAM_NUM; nSubCh++){
					if (memcmp(&(pVideoParam->nVParam[nCh].bs[nSubCh]), &(m_nRuntimeParam.nVParam[pVideoParam->nVParam[nCh].ch_num].bs[nSubCh]), sizeof(ST_BS_PARAM)) != 0){
						memcpy(&(m_nRuntimeParam.nVParam[pVideoParam->nVParam[nCh].ch_num].bs[nSubCh]), &(pVideoParam->nVParam[nCh].bs[nSubCh]), sizeof(ST_BS_PARAM));
						h264e_attr.dim.width = defRESOLUTIONS[m_nRuntimeParam.nVParam[pVideoParam->nVParam[nCh].ch_num].bs[nSubCh].size].width;
						h264e_attr.dim.height = defRESOLUTIONS[m_nRuntimeParam.nVParam[pVideoParam->nVParam[nCh].ch_num].bs[nSubCh].size].height;
						h264e_attr.frame_info.framerate = m_nRuntimeParam.nVParam[pVideoParam->nVParam[nCh].ch_num].bs[nSubCh].fps;
						h264e_attr.ratectl.mode = GM_MODE;
						h264e_attr.ratectl.gop = m_nRuntimeParam.nVParam[pVideoParam->nVParam[nCh].ch_num].bs[nSubCh].gop;
						h264e_attr.ratectl.bitrate = m_nRuntimeParam.nVParam[pVideoParam->nVParam[nCh].ch_num].bs[nSubCh].bps;
						if (m_nRuntimeParam.nVParam[pVideoParam->nVParam[nCh].ch_num].bs[nSubCh].quality >= 3)
							h264e_attr.ratectl.init_quant = 25;
						else if ((m_nRuntimeParam.nVParam[pVideoParam->nVParam[nCh].ch_num].bs[nSubCh].quality >= 2) &&
							(m_nRuntimeParam.nVParam[pVideoParam->nVParam[nCh].ch_num].bs[nSubCh].quality < 3))
							h264e_attr.ratectl.init_quant = 30;
						else
							h264e_attr.ratectl.init_quant = 45;
						h264e_attr.ratectl.bitrate_max = m_nRuntimeParam.nVParam[pVideoParam->nVParam[nCh].ch_num].bs[nSubCh].bps;
						h264e_attr.b_frame_num = 0;
						h264e_attr.enable_mv_data = 0;

						h264e_attr.profile_setting.profile = GM_H264E_HIGH_PROFILE;
						h264e_attr.profile_setting.level = GM_H264E_LEVEL_5_1;
						h264e_attr.profile_setting.config = GM_H264E_QUALITY_CONFIG;
						h264e_attr.profile_setting.coding = GM_H264E_CABAC_CODING;

						h264e_attr.ratectl.gop = m_nRuntimeParam.nVParam[pVideoParam->nVParam[nCh].ch_num].bs[nSubCh].fps * DSW_GOP_FIX;
						
						if (nSubCh == MAIN_STREAM)
							gm_set_attr(m_main_enc_object[nCh], &h264e_attr);
						else if (nSubCh == SUB_STREAM)
							gm_set_attr(m_sub_enc_object[nCh], &h264e_attr);
						else
							continue;
						if (gm_apply(m_groupfd) < 0){
							LOG4CPLUS_ERROR(log_dsw, "gm_apply fail, AP procedure something wrong!");
						}
					}
				}
				if (m_pOsds[nCh])
					m_pOsds[nCh]->HandleMessage(nMsgType, pParam);
			}
		}
		break;
	case PMSG_GET_VIDEOFRAME:
		return GetVideoFrame((LPST_VIDEO_FRAME_T)pParam);
		break;
	case PMSG_GET_VIDEOCODEC_CLASS:
		*((unsigned long*)pParam) = (unsigned long)this;
		break;
	case PMSG_SET_ALARMCAP_START:
		{
			LPST_ALARM_NODE_T pAlarmNode = (LPST_ALARM_NODE_T)pParam;
			if ((pAlarmNode->nBindV & MASK_BINDV_EXTCAM) == 0){
				LPST_ALARM_NODE_T pNode = GetCapNode(&m_nCapNodeList_UnUsed);
				if (pNode){
					char* pJpegData = pNode->pJpegData;
					memcpy(pNode, pParam, sizeof(ST_ALARM_NODE_T));
					pNode->pJpegData = pJpegData;
					PutCapNode(pNode, &m_nCapNodeList);
				}
				else
					LOG4CPLUS_DEBUG(log_dsw, "Not enough alarm cap node! drop this cap zone id [ " << ((LPST_ALARM_NODE_T)pParam)->sId << " ]");
			}
		}
		break;
	}
	return 0;
}

